﻿using System;
using System.Collections.Generic;
#if DEBUG
using System.Diagnostics;
#endif // #if DEBUG
using System.IO;
using System.Linq;
using System.Reflection;
using System.Runtime.InteropServices;
using System.Text;

using Microsoft.Office.Interop.Word;
using Word = Microsoft.Office.Interop.Word;

namespace FRL.DebtChased.Repository
{

	/// <summary>
	/// Declaration of WordSession class
	/// </summary>
	public class WordSession : IDisposable
	{

		/////////////////////////////////////////////////////////////
		// Attributes

		private Word.Application m_wordApp = null;

		private Word.Document m_wordDoc = null;

		/////////////////////////////////////////////////////////////
		// Construction

		public WordSession(string strFilepath, List<string> Dump = null)
		{

			// Create the application
			if (Dump!=null)
				Dump.Add("Creating Word.Application object");
			m_wordApp = new Word.Application();

			// If this is a new file...
			if (string.IsNullOrEmpty(strFilepath))
			{

				// Create a new word document
				if (Dump != null)
					Dump.Add("Creating empty Word document");
				m_wordDoc = m_wordApp.Documents.Add(Type.Missing, Type.Missing, Type.Missing, Type.Missing);

			}
			else
			{

				// Open an existing document
				if (Dump != null)
					Dump.Add("Opening Word document");
				m_wordDoc = m_wordApp.Documents.Open(strFilepath, false, false);

			}

		}

		~WordSession()      
		{

			// dispose of object
			Dispose();

		}

		public void Dispose()
		{

			// Note: The closing must be done this way to prevent
			// a ghost process remaining active

			// Close the document
			if (m_wordDoc != null)
				m_wordDoc.Close(false, Type.Missing, Type.Missing);

			// Quit the application
			if(m_wordApp != null)
				m_wordApp.Quit(false);

			// Release COM objects
			if (m_wordDoc != null)
				Marshal.ReleaseComObject(m_wordDoc);
			if(m_wordApp != null)
				Marshal.ReleaseComObject(m_wordApp);

			// Nullify handles
			m_wordDoc = null;
			m_wordApp = null;

			// garbage collect
			GC.Collect();

			// continue
			GC.SuppressFinalize(this);

		}

		/////////////////////////////////////////////////////////////
		// Operations

		public string GetAsText(int iMaxChars)
		{

			// shortcuts
			if (m_wordDoc == null)
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return null;
			}

			// Declare return variable
			string strFtmp = null;

			// Get context
			strFtmp = m_wordDoc.Content.Text;
			if (strFtmp.Length > iMaxChars)
				strFtmp = string.Format("{0}...",strFtmp.Substring(0, iMaxChars - 3));

			// Return variable
			return strFtmp;

		}

		public void InsertParagraph(bool bAtTop, string strText)
		{

			// shortcuts
			if (m_wordDoc == null)
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return;
			}

			// Create paragraph
			Word.Paragraph newPara = m_wordDoc.Content.Paragraphs.Add(Type.Missing);
			newPara.Range.Text = strText;
			if (bAtTop)
				newPara.Range.InsertParagraphAfter();
			else
				newPara.Range.InsertParagraphBefore();

		}

		public void SaveAs(string strFilepath)
		{

			// shortcuts
			if (m_wordDoc == null)
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return;
			}

			// If this is a docx file...
			if (strFilepath.EndsWith(".docx", StringComparison.CurrentCultureIgnoreCase))
			{

				// Call document function
				m_wordDoc.SaveAs2(strFilepath, Type.Missing, Type.Missing, Type.Missing, false);

			}
			else if (strFilepath.EndsWith(".pdf", StringComparison.CurrentCultureIgnoreCase))
			{

				// Setup PDF format
				m_wordDoc.SaveAs2(
					strFilepath, 
					Word.WdSaveFormat.wdFormatPDF,
					Type.Missing, 
					Type.Missing, 
					false
				);

			}
			
		}

		private void _replaceText(string strSearch, string strReplace)
		{

			// shortcuts
			if (m_wordDoc == null)
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return;
			}

			// Loop through the StoryRanges (sections of the Word doc)
			foreach (Word.Range currentRange in m_wordDoc.StoryRanges)
			{

				// Set the text to find and replace
				currentRange.Find.Text = strSearch;
				currentRange.Find.Replacement.Text = strReplace;
				currentRange.Find.Wrap = Word.WdFindWrap.wdFindContinue;

				// Execute the Find and Replace -- notice that the
				// 11th parameter is the "replaceAll" enum object
				object oMissing = Type.Missing;
				object replaceAll = Word.WdReplace.wdReplaceAll;
				currentRange.Find.Execute(
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref replaceAll,
					ref oMissing,
					ref oMissing,
					ref oMissing,
					ref oMissing
				);

			}

		}

		public bool Contains( string strSearch )
		{

			// shortcuts
			if (m_wordDoc == null)
			{
#if DEBUG
				Debug.Assert(false);
#endif // #if DEBUG
				return false;
			}

			// Declare return variable
			bool bFtmp = false;

			// Loop through the StoryRanges (sections of the Word doc)
			foreach (Word.Range currentRange in m_wordDoc.StoryRanges)
			{

				// Execute the Find and Replace -- notice that the
				// 11th parameter is the "replaceAll" enum object
				object oMissing = Type.Missing;
				object oSearch = strSearch;
				if (currentRange.Find.Execute(
					ref oSearch, ref oMissing, ref oMissing,
					ref oMissing, ref oMissing, ref oMissing, ref oMissing,
					ref oMissing, ref oMissing, ref oMissing, ref oMissing,
					ref oMissing, ref oMissing, ref oMissing, ref oMissing))
				{

					// Update flag and break out of loop
					bFtmp = true;
					break;

				}

			}

			// Return variable
			return bFtmp;

		}

		/////////////////////////////////////////////////////////////
		// Static operations

		public static string GetSummaryLine( string strFilepath, int iMaxChars )
		{

			// Declare return variable
			string strFtmp = null;

			// Declare a word session
			using (WordSession wordSession = new WordSession(strFilepath))
			{

				// Get the text
				strFtmp = wordSession.GetAsText(iMaxChars);

			}

			// Return variable
			return strFtmp;

		}

		public static string ReplaceFieldsAndSave(string strSourceFilepath, string strTargetFilepath, Dictionary<string, string> mapFields)
		{

			// Declare return variable
			string strError = null;

			try
			{

				// Open the source file
				using (WordSession wordSession = new WordSession(strSourceFilepath))
				{

					// If this document has fields...
					if (wordSession.Contains("[%"))
					{

						// Replace the fields
						foreach (var di in mapFields)
							wordSession._replaceText(di.Key, di.Value);

						// Check all fields have been replaced
						if (wordSession.Contains("[%"))
							strError = "Failed to replace all fields.";

					}

					// If that worked...
					if(strError==null)
					{

						// Save result as pdf
						wordSession.SaveAs(strTargetFilepath);

					}

				}

			}
			catch (Exception x)
			{

				// Process the error
				strError = ErrorProcessing.HandleException(x,"stuff word document",null);

			}

			// Return variable
			return strError;

		}

		public static string ConvertToPdf( string strSourceFilepath, string strTargetFilepath )
		{
#if DEBUG
			Debug.Assert(strSourceFilepath.EndsWith(".docx", StringComparison.CurrentCultureIgnoreCase));
			Debug.Assert(strTargetFilepath.EndsWith(".pdf", StringComparison.CurrentCultureIgnoreCase));
#endif // #if DEBUG

			// Declare return variable
			string strError = null;

			try
			{

				// Open the source file
				using (WordSession wordSession = new WordSession(strSourceFilepath))
				{

					// Save result as pdf
					wordSession.SaveAs(strTargetFilepath);

				}

			}
			catch (Exception x)
			{

				// Process the error
				strError = ErrorProcessing.HandleException(x, "convert word document", null);

			}

			// Return variable
			return strError;

		}

		public static List<string> RunDebugTest( string strTemplateFolderPath, string strClientFolderPath )
		{

			// Declare return variable
			List<string> Ftmp = new List<string>();

			try
			{

				// Loop through template files
				string[] strFiles = Directory.GetFiles(strTemplateFolderPath, "DebtChasedTemplate-*.docx");
				Ftmp.Add(string.Format("Found {0} Word templates", strFiles.Length));
				for (int i1 = 0; i1 < strFiles.Length; i1++)
				{

					// Get the summary line
					Ftmp.Add(string.Format("Reading summary of {0}", strFiles[i1]));
					string strSummaryLine = WordSession.GetSummaryLine(strFiles[i1], 100);
					Ftmp.Add(string.Empty);
					Ftmp.Add(string.Format("Summary line: {0}", strSummaryLine));
					Ftmp.Add(string.Empty);

					// Only do the first 5 in release mode
#if !DEBUG
					if (i1 > 5)
						break;
#endif // #if !DEBUG

				}

			}
			catch (Exception x)
			{

				// Dump the exception
				Ftmp.Add(string.Format("!!! EXCEPTION {0}", x.Message));
				if (x.InnerException != null)
					Ftmp.Add(string.Format("!!! INNER EXCEPTION {0}", x.InnerException.Message));

			}

			// Declare a template file
			string strDocFilepath = Path.Combine(strClientFolderPath, "test1.docx");
			if (File.Exists(strDocFilepath))
				File.Delete(strDocFilepath);

			try
			{

				// Declare a word session object
				using(WordSession wordSession = new WordSession(null, Ftmp))
				{

					// Add some text to the file
					Ftmp.Add("Adding text to document");
					wordSession.InsertParagraph(true, "New text at top of document. [%PRINTING_DATE%] [%CLIENT_NAME%] [%MULTILINE_ADDRESS%].  End of document.");

					// Saving document in templates folder
					Ftmp.Add(string.Format("Saving to {0}", strDocFilepath));
					wordSession.SaveAs(strDocFilepath);

					// Create the application
					Ftmp.Add("Destructing word session");

				}

			}
			catch(Exception x)
			{

				// Dump the exception
				Ftmp.Add(string.Format("!!! EXCEPTION {0}",x.Message));
				if(x.InnerException!=null)
					Ftmp.Add(string.Format("!!! INNER EXCEPTION {0}",x.InnerException.Message));

			}

			// Compose a target filepath
			string strTargetFilepath = Path.Combine(strClientFolderPath, "test1.pdf");
			if (File.Exists(strTargetFilepath))
				File.Delete(strTargetFilepath);

			// Stuff the document and save as a pdf
			Ftmp.Add("Stuffing the template.");
			Dictionary<string,string> mapFields = new Dictionary<string,string>();
			foreach (string strField in StufferDefinitions.Fields)
				mapFields.Add( 
					StufferDefinitions.AddSyntaxToField(strField),
					string.Format("__{0}__", strField) 
				);
			string strError = WordSession.ReplaceFieldsAndSave(strDocFilepath, strTargetFilepath, mapFields);
			if(strError != null)
				Ftmp.Add(strError);
			else
				Ftmp.Add(string.Format("Saved as {0}",strTargetFilepath));

			// Create the application
			Ftmp.Add("Word test finished");

			// Return variable
			return Ftmp;

		}

	}

}
